package com.lagou.java.factory;

import com.lagou.java.annotation.*;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 基于注解的bean工厂
 */

public class AnnotationConfigBeanFactory {

    private AnnotationConfigBeanFactory() {}

    private static AnnotationConfigBeanFactory annotationConfigBeanFactory = new AnnotationConfigBeanFactory();

    public static AnnotationConfigBeanFactory getInstance() {
        return annotationConfigBeanFactory;
    }

    private static Map<String, Object> map = new HashMap<>();

    public Object getBean (String beanId) {
        return map.get(beanId);
    }

    static {
        try {
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 遍历classpath下的所有.class文件
     * @param file
     * @param rootPath
     * @param classes
     * @throws Exception
     */
    public static void getClasses (File file, String rootPath, Set<String> classes) throws Exception {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
                getClasses(file1, rootPath, classes);
            }
        }
        if (file.getName().endsWith(".class")) {
//            System.out.println("absolutePath>>>" + file.getAbsolutePath());
//            System.out.println("rootPath>>>" + rootPath);
            //linux系统和windows系统路径不一致
            String className = file.getAbsolutePath()
                    .substring(rootPath.length()-
                            (file.getAbsolutePath().startsWith("/") ? 0: 1))
                    .replaceAll("\\\\", ".");
            classes.add(className.substring(0, className.lastIndexOf(".")));
//            System.out.println(className.substring(0, className.lastIndexOf(".")));
        }
    }

    public static void init() throws Exception {
//    public static void main(String[] args) throws Exception {
        String path = AnnotationConfigBeanFactory.class.getClassLoader().getResource("").getPath();
        //解决中文路径乱码
        path = URLDecoder.decode(path,"UTF-8");
        Set<String> classes = new HashSet<>();
        File file = new File(path);
        if(!file.exists() || file.listFiles().length == 0) {
            throw new RuntimeException("请重新编译项目后再运行...,");
        } else {
            getClasses(file, path, classes);
            System.out.println(classes.size());
        }

        //从classpath下扫描bean, 并存入map
        for (String aClass : classes) {
            Class<?> clazz = Object.class;
            try {
                clazz = Class.forName(aClass);
            } catch (Throwable e) {
                continue;
            }
            Annotation[] annotations = clazz.getAnnotations();

            String beanId = "";
            if(annotations.length >0) {
                for (Annotation annotation : annotations) {
                    String annoName = annotation.annotationType().getSimpleName();
//                    System.out.println("type>>>>" + annoName);
                    beanId =  clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
                    switch (annoName) {
                        case "Service":
                            Service service = clazz.getAnnotation(Service.class);
                            if(service.value() != null && !service.value().equals("")) beanId = service.value();
                            break;
                        case "Component":
                            Component component = clazz.getAnnotation(Component.class);
                            if(component.value() != null && !component.value().equals("")) beanId = component.value();
                            break;
                        case "Repository":
                            Repository repository = clazz.getAnnotation(Repository.class);
                            if(repository.value() != null && !repository.value().equals("")) beanId = repository.value();
                            break;
                        default:
                            beanId = null;
                    }
                    if(beanId != null) {
                        map.put(beanId, clazz.newInstance());
                    } else continue;
//                    System.out.println("type>>>>" + annoName);
                }
            } else continue;

        }
        //只有容器管理的bean才检查依赖注入
        //自动注入依赖bean
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Autowired annotation = field.getAnnotation(Autowired.class);
                if(annotation != null) {
//                    System.out.println("autowired>>>" + field.getName() + "=======" +annotation.value());
                    for (Object value : map.values()) {
                        boolean contains = Arrays.asList(value.getClass().getInterfaces()).stream()
                                .filter(claz -> claz.getName().equals(field.getType().getName()))
                                .collect(Collectors.toList()).size() > 0
                                || Arrays.asList(value.getClass(), value.getClass().getSuperclass()).stream()
                                .filter(claz -> claz.getName().equals(field.getType().getName()))
                                .collect(Collectors.toList()).size() > 0;
                        if(contains)field.set(map.get(entry.getKey()), value);
                    }
                } else continue;
            }

            //事务控制
            Method[] methods = entry.getValue().getClass().getMethods();
            Set<String> proxyMethods = new HashSet<>();
            for (Method method : methods) {
                method.setAccessible(true);
                Transactional annotation = method.getAnnotation(Transactional.class);
                if (annotation != null) {
                    proxyMethods.add(method.getName());
                }
            }
            //若类上加了注解@Transactional， 则为所有方法添加事务
            if(entry.getValue().getClass().getAnnotation(Transactional.class) != null) {
                List<String> allMethods = Arrays.stream(entry.getValue().getClass().getDeclaredMethods())
                        .map(method -> method.getName())
                        .collect(Collectors.toList());
                proxyMethods.addAll(allMethods);
            }

            //如果实现接口, 则用jdk动态代理, 如果未实现接口, 则用cglib
            Object proxyObj = null;
            if (proxyMethods.size() > 0) {
                if (entry.getValue().getClass().getInterfaces().length > 0) {
                    proxyObj = ((ProxyFactory)map.get("proxyFactory")).getJdkProxy(entry.getValue(), proxyMethods);
                } else {
                    proxyObj = ((ProxyFactory)map.get("proxyFactory")).getCglicProxy(entry.getValue(), proxyMethods);
                }
                //将原对象更新为代理对象
                map.put(entry.getKey(), proxyObj);
            } else continue;
        }

//        System.out.println(((TransactionManagerUtils)map.get("transactionManagerUtils")).getConnectionUtils());

    }

}

